package com.wheelerstreet.posterous.service
{
	import com.adobe.utils.DateUtil;
	import com.dynamicflash.util.Base64;
	import com.wheelerstreet.posterous.events.PosterousEvent;
	import com.wheelerstreet.posterous.models.Audio;
	import com.wheelerstreet.posterous.models.Comment;
	import com.wheelerstreet.posterous.models.Image;
	import com.wheelerstreet.posterous.models.ImageSource;
	import com.wheelerstreet.posterous.models.Post;
	import com.wheelerstreet.posterous.models.PosterousError;
	import com.wheelerstreet.posterous.models.Site;
	import com.wheelerstreet.posterous.models.Video;
	
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.FileReference;
	import flash.net.FileReferenceList;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	
	[Event(name="upload", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="uploadAndPost", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="uploadProgress", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="newpost", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="newcomment", type="com.wheelerstreet.posterous.events.PosterousEvent")]
		
	[Event(name="getSites", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="readPosts", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="getPost", type="com.wheelerstreet.posterous.events.PosterousEvent")]
	
	[Event(name="error", type="flash.events.Event")]
	
	public class Posterous extends EventDispatcher
	{
		public static var BASE_URL:String = "http://posterous.com/api/";
		// twitter
		protected static var UPLOAD:String 		= "upload";
		protected static var UPLOAD_AND_POST:String = "uploadAndPost";
		// posting
		protected static var NEW_POST:String 	= "newpost";
		protected static var NEW_COMMENT:String = "newcomment";
		// reading
		protected static var GET_SITES:String 	= "getsites";
		protected static var READ_POSTS:String 	= "readposts";
		// post.ly
		protected static var GET_POST:String 	= "getpost";
		
		// media types 
		public static var MEDIA_TYPE_IMAGE:String = "image";
		public static var MEDIA_TYPE_AUDIO:String = "audio";
		public static var MEDIA_TYPE_VIDEO:String = "video";
		
		// getters/setters
		protected var _username:String;
		protected var _password:String;
		protected var _source:String;
		protected var _sourceLink:String;
		// request info
		protected var _requestActive:Boolean;
		
		public function Posterous(username:String=null, password:String=null, source:String="as3-posterous-api", sourceLink:String="http://blog.wheelerstreet.com")
		{
			if(username)
				_username = username;
					
			if(password)
				_password = password;
			
			_source = source;
			_sourceLink = sourceLink;
		}
		/**
		 * Writing Data
		 * 
		 * POSTEROUS FILE UPLOADS FAIL
		 * 
		 * A note on file uploads from flash. It appears that authentication via 
		 * Basic HTTP Authentication fails when uploading via flash. I tried it and 
		 * like others out there ( http://docs.shopplugin.net/Basic_Auth_Breaks_Flash_Upload )
		 * was returned a 401 error. Until this is solved it won't be possible to 
		 * upload files to posterous. 
		 * 
		 */
		public function newPost(siteId:String, 
								title:String=null, 
								body:String=null, 
								autopost:Boolean=false, 
								isPrivate:Boolean=false, 
								date:Date=null, 
								tags:String=null):void
		{
			
			var vars:URLVariables = new URLVariables();
			vars.site_id = siteId;
			
			if(title)
				vars.title = title;
			if(body)
				vars.body = title;
			vars.autopost = autopost;
		
			vars.source=_source;
			vars.sourceLink = _sourceLink;
			
			makeRequest(NEW_POST,vars,true);
		}
		
		public function newComment(postId:Number, comment:String):void
		{
			var vars:URLVariables = new URLVariables();
			vars.post_id = postId;
			vars.comment = comment;
			makeRequest(NEW_COMMENT,vars,true);
		}
		/**
		 * File uploads work via the twitter authenticated API for now we only support individual 
		 * file uploads. 
		 */
		public function upload(twitterUsername:String, twitterPassword:String, media:FileReference=null, message:String=null, body:String=null):void
		{
			twitterUpload(UPLOAD,twitterUsername, twitterPassword, media, message, body);
		}
		 
		public function uploadAndPost(twitterUsername:String, twitterPassword:String,media:FileReference=null, message:String=null, body:String=null):void
		{
			twitterUpload(UPLOAD_AND_POST,twitterUsername, twitterPassword, media, message, body);
		}
		
		protected function twitterUpload(requestType:String,twitterUsername:String, twitterPassword:String,media:FileReference, message:String=null, body:String=null):void
		{
			var vars:URLVariables = new URLVariables();
			vars.username = twitterUsername;
			vars.password = twitterPassword;
			if(message)
				vars.message = message;
			if(body)
				vars.body = body;
			vars.source=_source;
			vars.sourceLink = _sourceLink;
			// in this case you don't need authentication the 
			// post is resolved by the users twitter account auth
			makeRequest(requestType, vars, false, media);
		}
		 
		/**
		 * Reading Data
		 */
		public function getSites():void
		{
			var vars:URLVariables = new URLVariables();
			vars.some = "varable"; // won't work if there is no variable
			makeRequest(GET_SITES,vars,true);
		}
		
		public function getPost(id:String):void
		{
			var vars:URLVariables = new URLVariables();
			vars.id = id;
			makeRequest(GET_POST,vars);
		}
		
		public function readPosts(siteId:String=null,hostname:String=null, numPosts:Number=10, page:Number=1,tag:String=null):void
		{
			var needsAuthentication:Boolean = true;
			
			var vars:URLVariables = new URLVariables();
			if(hostname || siteId) {
				
				vars.hostname = hostname;
				vars.num_posts = numPosts;
				vars.page = page;
				if(tag)
					vars.tag = tag;				
				needsAuthentication = false;
			}
			makeRequest(READ_POSTS,vars,needsAuthentication);
		}
		/**
		 * Encapsulated the type and url to allow for future changes
		 * this could be a factory in the future. 
		 */
		protected function getRequestURLFromType(type:String):URLRequest
		{
			// factory could create fullurl
			var fullurl:String = BASE_URL + type;			
			var request:URLRequest = new URLRequest(fullurl);
			request.method = URLRequestMethod.POST;
			return request;
		}
		
		
		
		protected function makeRequest(requestType:String, vars:URLVariables=null, needsAuthentication:Boolean=false, file:FileReference=null):void
		{
			var request:URLRequest = getRequestURLFromType(requestType);
			
			if(vars)
				request.data = vars;
				
			if(needsAuthentication)
				request.requestHeaders.push(requestHeader);
		
			if(file) {
				file.addEventListener(Event.CANCEL, cancelHandler);
	            file.addEventListener(Event.COMPLETE, completeHandler);
	            file.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
	            file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
	            file.addEventListener(Event.OPEN, openHandler);
	            file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
	            file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
	            file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,uploadCompleteDataHandler);
				file.upload(request,"media");
			
			} else {
				var loader:PosterousURLLoader = new PosterousURLLoader();
				loader.addEventListener(Event.COMPLETE, handleComplete,false,0,true);
				loader.addEventListener(IOErrorEvent.IO_ERROR, handleIOError, false,0,true);
				loader.requestType = requestType;
				loader.load(request);	
			}
			_requestActive = true;
			
			trace("authenticated: "+needsAuthentication+" sent: "+request.url);
		}
		
		protected function get requestHeader():URLRequestHeader
		{				
			var str:String = _username+":"+_password; 			
			return new URLRequestHeader("Authorization", "Basic " + Base64.encode(str));			
		}
		
		/**
		 * EVENT HANDLING 
		 */
		 
		private function cancelHandler(event:Event):void {
		    trace("cancelHandler: " + event);
		}
		
		private function completeHandler(event:Event):void {
		    trace("completeHandler: " + event);
		}
		
		private function uploadCompleteDataHandler(event:DataEvent):void {
		    trace("uploadCompleteData: " + event);
		}
		
		private function httpStatusHandler(event:HTTPStatusEvent):void {
		    trace("httpStatusHandler: " + event);
		}
		
		private function ioErrorHandler(event:IOErrorEvent):void {
		    trace("ioErrorHandler: " + event);
		}
		
		private function openHandler(event:Event):void {
		    trace("openHandler: " + event);
		}
		
		private function progressHandler(event:ProgressEvent):void {
		    var file:FileReference = FileReference(event.target);
		    trace("progressHandler name=" + file.name + " bytesLoaded=" + event.bytesLoaded + " bytesTotal=" + event.bytesTotal);
		}
		
		private function securityErrorHandler(event:SecurityErrorEvent):void {
		    trace("securityErrorHandler: " + event);
		}
		 
		 
		 /**
		 * URLLoader
		 */
		 
		protected function handleComplete(e:Event):void
		{
			var loader:PosterousURLLoader = e.target as PosterousURLLoader;
			var xml:XML = new XML(loader.data);
						
			trace("response: "+xml.@stat);			
			
			if(xml.@stat != "ok") {				
				dispatchEvent(new PosterousEvent(PosterousEvent.ERROR,parseError(xml) as Object,true));	
				return;
			}
						
		 	parseResults(loader.requestType, xml);
		}
		
		protected function parseResults(requestType:String, xml:XML):void
		{
			// trace(xml.post);
			var post:Post;
			var posts:Array;
			var sites:Array;
			
			switch(requestType) {
				case(NEW_POST): {
					post = parsePost(xml);
					dispatchEvent(new PosterousEvent(PosterousEvent.NEW_POST,post,true,false));
					break;	
				}				
				case(GET_POST): {					
					post = parsePost(xml);
					dispatchEvent(new PosterousEvent(PosterousEvent.GET_POST,post,true,false));
					break;
				}
				case(READ_POSTS): {
					posts = parsePosts(xml);
					dispatchEvent(new PosterousEvent(PosterousEvent.READ_POSTS,posts,true,false));
					break;
				}
				case(GET_SITES): {
					sites = parseSites(xml);
					dispatchEvent(new PosterousEvent(PosterousEvent.GET_SITES,sites,true,false));
					break;
				}
			}
			
				
				
		}
		
		protected function parseError(xml:XML):PosterousError
		{
			var error:PosterousError = new PosterousError();
			error.code = xml.err.@code;
			error.msg = xml.err.@msg;
			return error;
		}
	
		
		protected function parsePosts(xml:XML):Array
		{
			var _posts:Array =  [];			
			for each(var post:XML in xml.post) {	
				_posts.push(parsePost(post));
			}
			return _posts;
		}
		
		protected function parsePost(xml:XML):Post
		{
			var _post:Post = new Post();
			var post:XML = xml.post[0];
			if(post.hasOwnProperty("id"))
				_post.id = post.id;
			_post.url = post.url;		
			if(post.hasOwnProperty("link"))				
				_post.link = post.link;
			if(post.hasOwnProperty("longurl"))	
				_post.link = post.longurl;
			if(post.hasOwnProperty("views"))
				_post.views = post.views;
			if(post.hasOwnProperty("body"))			
				_post.body = post.body;
			if(post.hasOwnProperty("author"))
				_post.author = post.author;
			if(post.hasOwnProperty("authorpic"))	
				_post.authorPic = post.authorpic;
				
			_post.commentsEnabled = new Boolean(post.commentsenabled);
			
			if(post.hasOwnProperty("date"))
				_post.date = DateUtil.parseRFC822(post.date);
			if(xml.hasOwnProperty("comment"))
				_post.comments = parseComments(post.comment);
			if(xml.hasOwnProperty("media"))
				_post.media = parseMedia(xml.media);
				
			return _post;	
		}
		
		protected function parseMedia(media:XMLList):Array
		{
			var _media:Array = [];
			for each(var m:XML in media) {
				// image
				if(m.type == MEDIA_TYPE_IMAGE)
					_media.push(parseImage(m));
				// audio
				if(m.type == MEDIA_TYPE_AUDIO)
					_media.push(parseAudio(m));
				// video
				// NOTE: We convert to mp4 now, not flv. Old videos will have flv, new ones will have mp4 - (9/5) Sachin
				if(m.type == MEDIA_TYPE_VIDEO)
					_media.push(parseVideo(m));
				
			}
			return _media;
		}
		
		protected function parseImage(image:XML):Image
		{
			var _image:Image = new Image();
			for each(var i:XML in image.*) {
				var nodeName:String = i.name().toString();
				var src:ImageSource = new ImageSource();
				src.size = nodeName;
				src.filesize = i.filesize;
				src.url = i.url;
				src.height = i.height;
				src.width = i.width;
				if(nodeName == "medium") 
					_image.medium = src;
				if(nodeName == "thumb")
					_image.thumb = src;
			}
			return _image;
		}
		
		protected function parseAudio(audio:XML):Audio
		{
			var _audio:Audio = new Audio();
			_audio.url = audio.url;
			_audio.filesize = audio.filesize;
			_audio.artist = audio.artist;
			_audio.album = audio.album;
			_audio.song = audio.song;
			return _audio;	
		}
		
		protected function parseVideo(video:XML):Video
		{		
			var _video:Video = new Video();
			_video.url = video.url;
			_video.filesize = video.filesize;
			_video.thumb = video.thumb;
			_video.flv = video.flv;
			_video.mp4 = video.mp4;
			return _video;			
		}
		
		protected function parseComments(comments:XMLList):Array
		{
			var _comments:Array = [];
			for each(var comment:XML in comments) {				
				_comments.push(parseComment(comment));
			}
			return _comments;
		}
		
		protected function parseComment(comment:XML):Comment
		{
			var _comment:Comment = new Comment();
			if(comment.hasOwnProperty("id"))
				_comment.id = comment.id;
			if(comment.hasOwnProperty("body"))	
				_comment.body = comment.body;
			if(comment.hasOwnProperty("authorpic"))
				_comment.authorPic = comment.authorpic;
			if(comment.hasOwnProperty("author"))
				_comment.author = comment.author;
			if(comment.hasOwnProperty("date"))
				_comment.date = DateUtil.parseRFC822(comment.date);
			return _comment;
		}
		
		/**
		 * Sites
		 */
		protected function parseSites(xml:XML):Array
		{			
			var sites:Array = [];
			for each(var site:XML in xml.site) {
				var _site:Site = new Site();
				_site.id = site.id;
				_site.name = site.name;
				_site.url = site.url;
				_site.isPrivate = new Boolean(site['private']);
				_site.isPrimary = new Boolean(site.primary);
				sites.push(_site);
			}
			return sites;
		}

		protected function handleIOError(e:IOErrorEvent):void
		{			 
			trace("IOERROR: "+e.text);
		}
		
		public function get username():String
		{
			return _username;
		}
		
		public function set username(value:String):void
		{
			_username = value;
		}
		
		public function get password():String
		{
			return _password;
		}
		
		public function set password(value:String):void
		{
			_password = value;
		}
		
		protected function get authenticated():Boolean
		{
			return (_username && _password);
		}

	}
}